{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9df97d5b",
   "metadata": {},
   "source": [
    "## 基本概念"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74a36945",
   "metadata": {},
   "source": [
    "![](images/1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2adacb1e",
   "metadata": {},
   "source": [
    "装饰器来自 Decorator 的直译。什么叫装饰，就是装点、提供一些额外的功能。\n",
    "\n",
    "在 python 中的装饰器则是提供了一些额外的功能。装饰器本质上是一个Python函数（其实就是闭包），它可以让其他\n",
    "函数在不需要做任何代码变动的前提下增加额外功能，装饰器的返回值也是一个函数对象。\n",
    "\n",
    "装饰器用于有以下场景，比如：插入日志、性能测试、事务处理、缓存、权限校验等场景。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "712472d3",
   "metadata": {},
   "source": [
    "## 装饰器解决日志问题"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3161f52f",
   "metadata": {},
   "source": [
    "### v1.0版本解决"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "e254a396",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T02:59:08.044075Z",
     "start_time": "2023-10-13T02:59:08.039805Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "使用功能1\n",
      "日志记录\n",
      "使用功能2\n",
      "日志记录\n"
     ]
    }
   ],
   "source": [
    "def fun1():\n",
    "    print(\"使用功能1\")\n",
    "    print(\"日志记录\")\n",
    "\n",
    "\n",
    "def fun2():\n",
    "    print(\"使用功能2\")\n",
    "    print(\"日志记录\")\n",
    "    \n",
    "if __name__ == '__main__':\n",
    "    fun1()\n",
    "    fun2()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6dc2b8d7",
   "metadata": {},
   "source": [
    "### v2.0版本解决"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "18288d85",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:01:36.652782Z",
     "start_time": "2023-10-13T03:01:36.648268Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "使用功能1\n",
      "日志记录\n",
      "使用功能2\n",
      "日志记录\n"
     ]
    }
   ],
   "source": [
    "def writeLog():\n",
    "    print('日志记录')\n",
    "    \n",
    "def fun1():\n",
    "    print(\"使用功能1\")\n",
    "    writeLog()\n",
    "    \n",
    "def fun2():\n",
    "    print(\"使用功能2\")\n",
    "    writeLog()\n",
    "    \n",
    "if __name__ == '__main__':\n",
    "    fun1()\n",
    "    fun2()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98152478",
   "metadata": {},
   "source": [
    "### v3.0版本解决"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "70a42d7a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:03:01.750829Z",
     "start_time": "2023-10-13T03:03:01.746106Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "使用功能1\n",
      "日志纪录\n"
     ]
    }
   ],
   "source": [
    "def outfunc(func):\n",
    "    def infunc():\n",
    "        func()\n",
    "        print(\"日志纪录\")\n",
    "        \n",
    "    return infunc\n",
    "\n",
    "def fun1():\n",
    "    print(\"使用功能1\")\n",
    "    \n",
    "def fun2():\n",
    "    print(\"使用功能2\")\n",
    "    \n",
    "if __name__ == '__main__':\n",
    "    fun1 = outfunc(fun1)\n",
    "    \n",
    "    # 装饰器(闭包)\n",
    "    fun1()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e18f6af",
   "metadata": {},
   "source": [
    "### v4.0版本解决，装饰器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "0123555d",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:04:49.876105Z",
     "start_time": "2023-10-13T03:04:49.866087Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "使用功能1\n",
      "日志纪录\n",
      "使用功能2 100 200\n",
      "日志纪录\n"
     ]
    }
   ],
   "source": [
    "# 第一个装饰器\n",
    "def mylog(func):\n",
    "\n",
    "    # 可以传入参数\n",
    "    def inner(*args, **kwargs):\n",
    "        func(*args, **kwargs)\n",
    "        print(\"日志纪录\")\n",
    "\n",
    "    return inner\n",
    "\n",
    "\n",
    "@mylog    # fun1 = mylog(fun1)\n",
    "def fun1():\n",
    "    print(\"使用功能1\")\n",
    "\n",
    "\n",
    "@mylog    # fun2 = mylog(fun2)\n",
    "def fun2(a, b):\n",
    "    print(\"使用功能2\", a, b)\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    fun1()\n",
    "    fun2(100, 200)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "193617da",
   "metadata": {},
   "source": [
    "## 多个装饰器执行顺序"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58fd81bd",
   "metadata": {},
   "source": [
    "有时候，我们需要多个装饰器修饰一个函数。比如：需要增加日志功能、增加执行效率测试功能。\n",
    "\n",
    "装饰器函数的执行顺序是分为（被装饰函数）定义阶段和（被装饰函数）执行阶段的，装饰器函数在被装饰函数定义好后立即执行\n",
    "\n",
    "- 在函数定义阶段：执行顺序是从最靠近函数的装饰器开始，自内而外的执行\n",
    "- 在函数执行阶段：执行顺序由外而内，一层层执行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "3a14d7f2",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:08:55.401605Z",
     "start_time": "2023-10-13T03:08:52.387318Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cost time start\n",
      "cost time end\n",
      "mylog start\n",
      "mylog end\n",
      "日志纪录 start\n",
      "开始计时..\n",
      "fun2, start\n",
      "fun2, end\n",
      "耗费时间:3.0053670406341553\n",
      "日志纪录 end \n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "\n",
    "def mylog(func):\n",
    "    print(\"mylog start\")\n",
    "\n",
    "    def infunc():\n",
    "        print(\"日志纪录 start\")\n",
    "        func()\n",
    "        print(\"日志纪录 end \")\n",
    "        \n",
    "    print(\"mylog end\")\n",
    "    return infunc\n",
    "\n",
    "\n",
    "def cost_time(func):\n",
    "    print(\"cost time start\")\n",
    "\n",
    "    def infunc():\n",
    "        print(\"开始计时..\")\n",
    "        start = time.time()\n",
    "        func()\n",
    "        end = time.time()\n",
    "        print(f\"耗费时间:{end-start}\")\n",
    "\n",
    "    print(\"cost time end\")\n",
    "    return infunc\n",
    "\n",
    "\n",
    "@mylog\n",
    "@cost_time   # fun2 = mylog(cost_time(fun2))\n",
    "def fun2():\n",
    "    print(\"fun2, start\")\n",
    "    time.sleep(3)\n",
    "    print(\"fun2, end\")\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    fun2()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c44f7495",
   "metadata": {},
   "source": [
    "## 带参数的装饰器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "57c80a28",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:12:07.141658Z",
     "start_time": "2023-10-13T03:12:07.133893Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "文件中：日志纪录\n",
      "使用功能2 100 200\n"
     ]
    }
   ],
   "source": [
    "\"\"\"带参数的装饰器的典型写法\"\"\"\n",
    "\n",
    "def mylog(type):\n",
    "    \n",
    "    def decorator(func):\n",
    "        def infunc(*args, **kwargs):\n",
    "            if type == \"文件\":\n",
    "                print(\"文件中：日志纪录\")\n",
    "            else:\n",
    "                print(\"控制台：日志纪录\")\n",
    "            return func(*args, **kwargs)\n",
    "        return infunc\n",
    "    \n",
    "    return decorator\n",
    "\n",
    "\n",
    "@mylog(\"文件\")\n",
    "def fun2(a, b):\n",
    "    print(\"使用功能2\", a, b)\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    fun2(100, 200)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff5a1a85",
   "metadata": {},
   "source": [
    "## wraps装饰器\n",
    "一个函数不止有他的执行语句，还有着 __name__ （函数名）， __doc__（说明文档）等属性，我们之前的例子会导致这些属性改变。\n",
    "\n",
    "functool.wraps 可以将原函数对象的指定属性赋值给包装函数对象，默认有module、name、doc，或者通过参数选择。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "595c40c0",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:15:01.986294Z",
     "start_time": "2023-10-13T03:15:01.981306Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "日志纪录...\n",
      "函数文档: 强大的功能2\n",
      "使用功能2\n",
      "函数文档 --> 强大的功能2\n"
     ]
    }
   ],
   "source": [
    "\"\"\"wraps装饰器的用法\"\"\"\n",
    "from functools import wraps\n",
    "\n",
    "def mylog(func):\n",
    "    \n",
    "    @wraps(func)\n",
    "    def infunc(*args, **kwargs):\n",
    "        print(\"日志纪录...\")\n",
    "        print(\"函数文档:\", func.__doc__)\n",
    "        return func(*args, **kwargs)\n",
    "    \n",
    "    return infunc\n",
    "\n",
    "\n",
    "@mylog  # fun2 = mylog(fun2)\n",
    "def fun2():\n",
    "    \"\"\"强大的功能2\"\"\"\n",
    "    print(\"使用功能2\")\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    fun2()\n",
    "    print(\"函数文档 -->\", fun2.__doc__)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2fe2d410",
   "metadata": {},
   "source": [
    "## 内置装饰器\n",
    "我们在面向对象学习时，学习过三种装饰器： property、staticmethod、classmethod \n",
    "\n",
    "### property装饰器\n",
    "property 装饰器用于类中的函数，使得我们可以像访问属性一样来获取一个函数的返回值。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "614e5f5a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:17:17.846390Z",
     "start_time": "2023-10-13T03:17:17.841970Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "360000\n"
     ]
    }
   ],
   "source": [
    "class User:\n",
    "    \"\"\"prperty装饰器的使用\"\"\"\n",
    "    \n",
    "    def __init__(self, name, month_salary):\n",
    "        self.name = name\n",
    "        self.month_salary = month_salary\n",
    "        \n",
    "    @property\n",
    "    def year_salary(self):\n",
    "        return int(self.month_salary) * 12\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    u1 = User(\"zxy\", \"30000\")\n",
    "    print(u1.year_salary)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b19dd76",
   "metadata": {},
   "source": [
    "### staticmethod装饰器\n",
    "staticmethod 装饰器同样是用于类中的方法，这表示这个方法将会是一个静态方法，意味着该方法可以直接被调用无需实例化，但同样意味着它没有 self 参数，也无法访问实例化后的对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "96f608cc",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:20:15.820775Z",
     "start_time": "2023-10-13T03:20:15.817333Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world!\n"
     ]
    }
   ],
   "source": [
    "class Person:\n",
    "    \"\"\"staticmethod装饰器的使用\"\"\"\n",
    "    \n",
    "    @staticmethod\n",
    "    def say_hello():\n",
    "        print(\"hello world!\")\n",
    "        \n",
    "if __name__ == '__main__':\n",
    "    Person.say_hello()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "91824a23",
   "metadata": {},
   "source": [
    "### classmethod装饰器\n",
    "classmethod 这个方法是一个类方法。该方法无需实例化，没有 self 参数。\n",
    "\n",
    "相对于 staticmethod 的区别在于它会接收一个指向类本身的 cls 参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "be3dccd2",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:22:22.357027Z",
     "start_time": "2023-10-13T03:22:22.350917Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我是Person\n",
      "hello world!\n"
     ]
    }
   ],
   "source": [
    "class Person:\n",
    "    \"\"\"classmethod装饰器\"\"\"\n",
    "\n",
    "    @classmethod\n",
    "    def say_hello(cls):\n",
    "        print(f\"我是{ cls.__name__ }\")\n",
    "        print(\"hello world!\")\n",
    "        \n",
    "if __name__ == '__main__':\n",
    "    Person.say_hello()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "165cfe09",
   "metadata": {},
   "source": [
    "## 类装饰器\n",
    "上面写的装饰器都是函数来完成的。我们用类也可以实现装饰器。\n",
    "\n",
    "类能实现装饰器的功能， 是由于当我们调用一个对象时，实际上调用的是它的 __call__ 方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "6f276e95",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:24:01.825623Z",
     "start_time": "2023-10-13T03:24:01.822150Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我是 Demo\n"
     ]
    }
   ],
   "source": [
    "class Demo:\n",
    "    \"\"\"call方法的调用\"\"\"\n",
    "    \n",
    "    def __call__(self):\n",
    "        print('我是 Demo')\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    demo = Demo()\n",
    "    demo() # 直接调用对象，实质是调用了他的__call__()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "5a94f808",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:26:00.925538Z",
     "start_time": "2023-10-13T03:26:00.920550Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "日志纪录...\n",
      "使用功能2...\n"
     ]
    }
   ],
   "source": [
    "class MyLogDecorator():\n",
    "    \"\"\"类装饰器的使用\"\"\"\n",
    "    def __init__(self, func):\n",
    "        self.func = func\n",
    "\n",
    "    def __call__(self, *args, **kwargs):\n",
    "        print(\"日志纪录...\")\n",
    "        return self.func(*args, **kwargs)\n",
    "\n",
    "\n",
    "@MyLogDecorator     # fun2 = MyLogDecorator(fun2)\n",
    "def fun2():\n",
    "    print(\"使用功能2...\")\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    fun2()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21d2842d",
   "metadata": {},
   "source": [
    "## 练习：缓存装饰器和计时装饰器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "05b4b632",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-13T03:30:39.648336Z",
     "start_time": "2023-10-13T03:30:36.629451Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "缓存装饰器初始化...\n",
      "start func1\n",
      "end func1\n",
      "耗时:3.005307912826538\n",
      "耗时:6.9141387939453125e-06\n",
      "999\n",
      "999\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "\n",
    "class CacheDecorator():\n",
    "    \"\"\"缓存装饰器\"\"\"\n",
    "\n",
    "    __cache = {}\n",
    "\n",
    "    def __init__(self, func):\n",
    "        print('缓存装饰器初始化...')\n",
    "        self.func = func\n",
    "\n",
    "    def __call__(self, *args, **kwargs):\n",
    "\n",
    "        # 如果缓存中有对应的方法名，则直接返回对应的返回值\n",
    "        if self.func.__name__ in CacheDecorator.__cache:\n",
    "            return CacheDecorator.__cache[self.func.__name__]\n",
    "\n",
    "        # 如果缓存中没有对应的方法名，则进行计算，并将结果缓存\n",
    "        else:\n",
    "            result = self.func(*args, **kwargs)\n",
    "            CacheDecorator.__cache[self.func.__name__] = result\n",
    "            return result\n",
    "\n",
    "\n",
    "def cost_time(func):\n",
    "    \"\"\"计时装饰器\"\"\"\n",
    "    \n",
    "    def infunc(*args, **kwargs):\n",
    "        \n",
    "        start = time.time()\n",
    "        result = func(*args, **kwargs)\n",
    "        end = time.time()\n",
    "        \n",
    "        print(f\"耗时:{end-start}\")\n",
    "        \n",
    "        return result\n",
    "    \n",
    "    return infunc\n",
    "\n",
    "\n",
    "@cost_time\n",
    "@CacheDecorator\n",
    "def func1_long_time():\n",
    "    \"\"\"模拟耗时较长，每次执行返回结果都一样的情况\"\"\"\n",
    "    print(\"start func1\")\n",
    "    time.sleep(3)\n",
    "    print(\"end func1\")\n",
    "    return 999\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    r1 = func1_long_time()\n",
    "    r2 = func1_long_time()\n",
    "    print(r1)\n",
    "    print(r2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50065cb4",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "248.1875px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
